"""
starfield.py

Classes and other code to implement the starfield for AsciiBlast.
This module is intended to be used by the main file, but if run
directly it will execute unit tests
"""

import random
import curses
from gameobject import Gameobject

class Star(Gameobject):
    """Simple class to represent a single star."""
    def __init__(self, gameobject, width, height = None):
        """Set the position of the star to a random position."""
        self._gameobject = gameobject
        self.half_position = False
        self.x = random.randrange(width)
        if height == None:
            self.y = random.randrange(height)
        else:
            self.y = height
        self.set_zdistance_properties() # randomnly choose to be nearer or not

    def draw(self, stdscr):
        """Draws the star to the curses screen."""
        draw_attributes = curses.A_DIM
        if self.is_nearer:
            draw_attributes = curses.A_BOLD
        if self.half_position == False:
            stdscr.addstr(self.y, self.x, "'", draw_attributes)
        else:
            stdscr.addstr(self.y, self.x, ".", draw_attributes)

    def tick(self, tickcount):
        if tickcount % self._move_rate == 0:
            # It's time for the star to move
            if self.half_position == False:
                self.half_position = True
            else:
                self.half_position = False
                self.y += 1

        # check if we've moved off screen. if so reset outselves
        if self.y >= self._gameobject.screen_height:
            self.half_position = False
            self.x = random.randrange(self._gameobject.screen_width)
            self.y = 0
            self.set_zdistance_properties()

    def set_zdistance_properties(self):
        """
        Randomnly (1 in 4) set this star to be "nearer".

        If the star is nearer then it is drawn brighter and moves
        more quickly. More distant stars do the opposite of course.
        """
        self.is_nearer = random.randrange(4) == 0
        if self.is_nearer:
            self._move_rate = 100        # scroll every 0.2s
        else:
            self._move_rate = 200        # scroll every 0.3s

    def dump(self):
        """Debug function to display diagnostic info to stdout."""
        print self.x, self.y, self.half_position


class Starfield(Gameobject):
    """
    Class to implement the starfield background.

    The starfield consists of a number of star objects. The main job
    of the starfield, apart from initialising the set of stars, is to handle
    the scrolling down of them as time passes, removing old ones as they
    drop off and creating new ones.
    """
    _starfield = []
    _star_count = 0
    _move_rate = 200            # scroll every 0.2s

    def __init__(self, gameobject):
        """
        Initialise the starfield.

        screen_width determines the range that the x-value of a star can be.
        screen_height similar determines the y-value, but it also controls
        the number of stars created. The starfield will contains one star per
        line so the number of stars will also be screen_height.
        """
        self._gameobject = gameobject
        self._star_count = gameobject.screen_height
        # build the array of stars
        for x in range(self._star_count):
            self._starfield.append(Star(gameobject, gameobject.screen_width, x))

    def tick(self, tickcount):
        """
        Tells the starfield that the game has moved one 'tick'.

        For most ticks the starfield need do nothing, but every so often
        the starfield must move down. For stars which move off the bottom
        of the screen, this means generating a new star.
        """
        for star in self._starfield:
            star.tick(tickcount)

    def draw(self, stdscr):
        """Draw all the stars in the starfield to the curses screen."""
        for star in self._starfield:
            star.draw(stdscr)

    def dump(self):
        """Print out information about the starfield. Debug function."""
        print "starfield contains ", len(self._starfield), " stars"
        print "x,y values are:"
        for star in self._starfield:
            print "-",
            star.dump()

# If the module is being run as the main program then execute tests.
if __name__ == '__main__':
    print "Tests go here..."
    # todo: create mock gameobject?
    """
    test_starfield = Starfield(80, 3) # simulate 0...79, 0...5
    test_starfield.dump()
    test_starfield.tick(200)
    test_starfield.dump()
    test_starfield.tick(400)
    test_starfield.dump()
    test_starfield.tick(600)
    test_starfield.dump()
    test_starfield.tick(800)
    test_starfield.dump()
    print "Tests run!"
    """
